class Annotate {
  static activeTool = 1;
  static undoOperation = [];
  static redoOperation = [];
  static color = '#212121';
  static borderColor = '#000000';
  static fillColor = 'rgba(0, 0, 0, 0)';
  static borderSize = 1;
  static fontSize = 16;
  static width = 0;
  static height = 0;
  static scale = 1;
  static oldLeft = -1;
  static oldTop = -1;
  static commentLeft = -1;
  static commentTop = -1;
  static canvas = null;
  static mouseFrom = {};
  static mouseTo = {};
  static moveCount = 1;
  static doDrawing = false;
  static drawingObject = null;
  static mouseOutFlag = false;
  static undo = true;
  static brushs = {};
  static storages = {};
  static messages = [];
  static processingMessage = false;
  static bordersAndControlsOptions = {
    padding: 3,
    borderScaleFactor: 3,
    cornerSize: 10,
    cornerBackgroundColor: 'black',
    borderColor: '#00BFFF',
    cornerColor: '#00BFFF',
    cornerStyle: 'circle',
    transparentCorners: true
  };
  static hasBordersAndControls = true;
  static ActiveTool = {
    Selector: 0,
    Pencil: 1,
    Text: 2,
    Rectangle: 3,
    Circle: 4,
    Lead: 5,
    Image: 6
  };
  constructor(id, copyFrom, page) {
    let canvas = new fabric.Canvas(id, {
      enablePointerEvents: true,
      freeDrawingBrush: {
        width: 1,
        color: Annotate.color
      }
    });
    Annotate.canvas = canvas;
    Annotate.page = page;
    this.canvas = canvas;
    canvas.selection = false;
    canvas.on('object:added', e => {
      let target = e.target;
      if (target.annotationId) {
        if (Annotate.undo) {
          Annotate.addUndo({
            id: target.annotationId,
            before: null,
            after: Annotate.toJSON(target)
          });
        }
        Annotate.undo = true;
      }
    });

    canvas.on('object:rotating', e => {
      let obj = e.target;
      Annotate.notifyUpdateItem({
        annotationId: obj.annotationId,
        annotationType: obj.annotationType,
        angle: obj.angle,
        left: obj.left,
        top: obj.top
      });
      Annotate.notifyLatestItem(obj);
    });

    canvas.on('object:scaling', e => {
      let obj = e.target;
      obj.strokeUniform = true;
      if (!obj.strokeWidthUnscaled && obj.strokeWidth) {
        obj.strokeWidthUnscaled = obj.strokeWidth;
      }
      if (obj.strokeWidthUnscaled) {
        obj.strokeWidth = obj.strokeWidthUnscaled;
      }
      Annotate.notifyUpdateItem({
        annotationId: obj.annotationId,
        annotationType: obj.annotationType,
        scaleX: obj.scaleX,
        scaleY: obj.scaleY,
        strokeWidth: obj.strokeWidth,
        strokeWidthUnscaled: obj.strokeWidthUnscaled,
        strokeUniform: true
      });
      Annotate.notifyLatestItem(obj);
      Annotate.createControlPanel(canvas, obj);
    });
    canvas.on('object:removed', () => {
      $('.controlPanel').remove();
      Annotate.updateUndoRedoCss();
    });
    canvas.on('object:moving', e => {
      let obj = e.target;
      Annotate.createControlPanel(canvas, obj);
      Annotate.movingComment(obj);
      Annotate.notifyUpdateItem({
        annotationId: obj.annotationId,
        annotationType: obj.annotationType,
        left: obj.left,
        top: obj.top
      });
      Annotate.notifyLatestItem(obj);
    });

    canvas.on('selection:created', e => {
      e.selected.forEach(selected => {
        Annotate.createControlPanel(canvas, selected);
        $('.conmentPanel').remove();
        Annotate.activeTool = Annotate.ActiveTool.Selector;
        Annotate.activeToolButton(Annotate.ActiveTool.Selector);

        if (
          selected.annotationType === 'lead' ||
          selected.annotationType === 'path' ||
          selected.annotationType === 'rect' ||
          selected.annotationType === 'image' ||
          selected.annotationType === 'circle'
        ) {
          $.each(Annotate.canvas.getObjects(), (index, object) => {
            if ('comment' + selected.annotationId === object.annotationId) {
              Annotate.oldLeft = selected.left;
              Annotate.oldTop = selected.top;
              Annotate.commentLeft = object.left;
              Annotate.commentTop = object.top;
              return false;
            }
          });
        }
      });
    });
    canvas.on('selection:updated', e => {
      e.selected.forEach(selected => {
        Annotate.createControlPanel(canvas, selected);
        $('.conmentPanel').remove();
        Annotate.activeTool = Annotate.ActiveTool.Selector;
        Annotate.activeToolButton(Annotate.ActiveTool.Selector);
        if (
          selected.annotationType === 'lead' ||
          selected.annotationType === 'path' ||
          selected.annotationType === 'rect' ||
          selected.annotationType === 'image' ||
          selected.annotationType === 'circle'
        ) {
          $.each(Annotate.canvas.getObjects(), (index, object) => {
            if ('comment' + selected.annotationId === object.annotationId) {
              Annotate.oldLeft = selected.left;
              Annotate.oldTop = selected.top;
              Annotate.commentLeft = object.left;
              Annotate.commentTop = object.top;
              return false;
            }
          });
        }
      });
    });
    canvas.on('selection:cleared', () => {
      $('.controlPanel').remove();
    });
    let before = null;
    canvas.on('mouse:down', options => {
      if (Annotate.activeTool === Annotate.ActiveTool.Selector) {
        before = Annotate.toJSON(Annotate.canvas.getActiveObject());
      }
      let xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
      Annotate.mouseFrom.x = xy.x;
      Annotate.mouseFrom.y = xy.y;
      Annotate.updateActiveTool(Annotate.mouseFrom.x, Annotate.mouseFrom.y);
      Annotate.doDrawing = true;
      if (canvas.isDrawingMode) {
        Annotate.penId = Annotate.uuid();
        Annotate.notifyPenEvent({
          annotationId: Annotate.penId,
          point: { x: xy.x, y: xy.y },
          event: 'mousedown',
          color: Annotate.color,
          borderSize: Annotate.borderSize
        });
      }

      $.each(canvas.getObjects(), (index, object) => {
        if (object.annotationType === 'text') {
          if (Annotate.mouseOutFlag === true) {
            Annotate.mouseOutFlag = false;
            object.exitEditing();
          }
        }
        canvas.requestRenderAll();
      });
    });
    canvas.on('mouse:move', options => {
      if (!Annotate.doDrawing) {
        return;
      }
      let xy;
      if (canvas.isDrawingMode) {
        xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
        Annotate.notifyPenEvent({
          annotationId: Annotate.penId,
          point: { x: xy.x, y: xy.y },
          event: 'mousemove'
        });
        return;
      }
      Annotate.moveCount++;
      if (Annotate.moveCount % 10 === 0) {
        xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
        Annotate.mouseTo.x = xy.x;
        Annotate.mouseTo.y = xy.y;
        Annotate.drawing();
      }
    });
    canvas.on('mouse:up', options => {
      let after = Annotate.toJSON(Annotate.canvas.getActiveObject());
      if (after && JSON.stringify(after) !== JSON.stringify(before)) {
        Annotate.addUndo({
          id: after.annotationId,
          before: before,
          after: after
        });
      }
      let xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
      Annotate.mouseTo.x = xy.x;
      Annotate.mouseTo.y = xy.y;
      Annotate.drawingObject = null;
      Annotate.moveCount = 1;
      Annotate.doDrawing = false;
      if (canvas.isDrawingMode) {
        xy = Annotate.transformMouse(options.e.offsetX, options.e.offsetY);
        Annotate.notifyPenEvent({
          annotationId: Annotate.penId,
          event: 'mouseup'
        });
        let len = Annotate.canvas.getObjects().length;
        let item = Annotate.canvas.item(len - 1);
        item.annotationId = Annotate.penId;
        item.annotationType = 'path';
        Annotate.notifyAddItem(item);
        Annotate.addUndo({
          id: item.annotationId,
          before: null,
          after: Annotate.toJSON(item)
        });
      }
    });

    if (copyFrom) {
      canvas.loadFromJSON(copyFrom.canvas.toJSON(['annotationId', 'annotationType']));
      copyFrom.release();
    }
  }

  release() {
    this.canvas.clear();
    this.canvas.dispose();
    $(this.canvas.wrapperEl).remove();
    this.canvas['__eventListeners'] = {};
  }

  static movingComment(obj) {
    if (
      obj.annotationType === 'lead' ||
      obj.annotationType === 'path' ||
      obj.annotationType === 'rect' ||
      obj.annotationType === 'image' ||
      obj.annotationType === 'circle'
    ) {
      $.each(Annotate.canvas.getObjects(), (index, object) => {
        if ('comment' + obj.annotationId === object.annotationId) {
          let newLeft = obj.left;
          let newTop = obj.top;
          let distanceLeft = newLeft - Annotate.oldLeft;
          let distanceTop = newTop - Annotate.oldTop;
          object.set('left', Annotate.commentLeft + distanceLeft);
          object.set('top', Annotate.commentTop + distanceTop);
          object.setCoords();
          Annotate.notifyUpdateItem({
            annotationId: object.annotationId,
            annotationType: object.annotationType,
            left: object.left,
            top: object.top
          });
          Annotate.notifyLatestItem(object);
          Annotate.canvas.bringForward(object);
          Annotate.canvas.requestRenderAll();
          return false;
        }
      });
    }
  }

  static drawing() {
    let obj = Annotate.drawingObject;
    let canvas = Annotate.canvas;
    if (obj) {
      canvas.remove(obj);
      Annotate.notifyRemoveItem(obj);
      if (Annotate.undo) {
        Annotate.undoOperation.pop();
        Annotate.updateUndoRedoCss();
      }
      Annotate.undo = true;
    }
    let canvasObject = null;
    switch (Annotate.activeTool) {
      case Annotate.ActiveTool.Circle:
        canvasObject = Annotate.createCircle(Annotate.mouseFrom, Annotate.mouseTo);
        Annotate.notifyAddItem(canvasObject);
        canvas.selection = false;
        break;
      case Annotate.ActiveTool.Rectangle:
        canvasObject = Annotate.createRect(Annotate.mouseFrom, Annotate.mouseTo);
        Annotate.notifyAddItem(canvasObject);
        canvas.selection = false;
        break;
      default:
        break;
    }
    Annotate.drawingObject = canvasObject;
  }

  static waitLoaded() {
    return new Promise(resolve => {
      if (Annotate.canvas) {
        resolve();
      } else {
        let interval = setInterval(() => {
          if (Annotate.canvas) {
            resolve();
            clearInterval(interval);
          }
        }, 20);
      }
    });
  }

  static async processOneUpdateMessage() {
    Annotate.processingMessage = true;
    if (Annotate.messages.length === 0) {
      Annotate.processingMessage = false;
      return;
    }
    let data = null;
    Annotate.messages.some((item, index) => {
      if (item.page === Annotate.page) {
        data = Annotate.messages.splice(index, 1)[0];
        return true;
      }
    });
    if (data === null) {
      Annotate.processingMessage = false;
      return;
    }
    await Annotate.waitLoaded();
    let item = data.item;
    let action = data.action;
    Annotate.undo = false;
    switch (action) {
      case 'pen':
        let brush;
        let options = {
          e: {
            isPrimary: true
          }
        };
        switch (data.item.event) {
          case 'mousedown':
            brush = new fabric.PencilBrush(Annotate.canvas);
            brush.color = item.color;
            brush.width = item.borderSize;
            Annotate.brushs[data.id] = brush;
            brush.onMouseDown(item.point, options);
            break;
          case 'mousemove':
            brush = Annotate.brushs[data.id];
            if (brush) {
              brush.onMouseMove(item.point, options);
            }
            break;
          case 'mouseup':
            brush = Annotate.brushs[data.id];
            if (brush) {
              brush.onMouseUp(options);
              let len = Annotate.canvas.getObjects().length;
              Annotate.canvas.item(len - 1).annotationId = item.annotationId;
              Annotate.canvas.item(len - 1).annotationType = 'path';
            }
            break;
          default:
            break;
        }
        break;
      case 'add':
        if (data.storage) {
          Annotate.storages[item.annotationId] = item;
        }
        let exist = false;
        $.each(Annotate.canvas.getObjects(), (index, obj) => {
          if (obj.annotationId === item.annotationId) {
            $.each(item, (key, val) => {
              if (obj[key] !== val) {
                obj.set(key, val);
              }
            });
            exist = true;
            return false;
          }
        });
        if (!exist) {
          switch (item.annotationType) {
            case 'path':
              Annotate.createPath(item);
              break;
            case 'comment':
              Annotate.createText(item);
              break;
            case 'text':
              Annotate.createText(item);
              break;
            case 'lead':
              Annotate.createLeadWithOptions(item);
              break;
            case 'rect':
              Annotate.createRectWithOptions(item);
              break;
            case 'circle':
              Annotate.createCircleWithOptions(item);
              break;
            case 'image':
              await Annotate.createImage(item);
              break;
            default:
              break;
          }
        }
        break;
      case 'update':
        $.each(Annotate.canvas.getObjects(), (index, obj) => {
          if (item.annotationId === obj.annotationId) {
            $.each(item, (key, val) => {
              if (obj[key] !== val) {
                obj.set(key, val);
              }
            });
            Annotate.canvas.requestRenderAll();
            return false;
          }
        });
        break;
      case 'remove':
        $.each(Annotate.canvas.getObjects(), (index, obj) => {
          if (item.annotationId === obj.annotationId) {
            Annotate.canvas.remove(obj);
            return false;
          }
        });
        break;
      default:
        break;
    }
    Annotate.processOneUpdateMessage();
  }

  static processUpdateMessage() {
    if (Annotate.processingMessage) {
      return false;
    }
    Annotate.processOneUpdateMessage();
    if (Annotate.messages.length === 0) {
      return true;
    }
    return false;
  }

  static onUpdate(data) {
    Annotate.messages.push(data);
    Annotate.processUpdateMessage();
  }

  static updatePage(canvas, page) {
    Annotate.canvas = canvas;
    Annotate.page = page;
    Annotate.processUpdateMessage();
    // 更新toolbar状态
    Annotate.updateToobar();
  }

  static updateToobar() {
    // 设置hand选中
    $( "#toolButtonHand" ).click();
  }

  static notifyItemAction(action, item, renew, clear, deliver, storage) {
    try {
      let update = {
        action: action,
        id: item.annotationId,
        item: item.toJSON ? Annotate.toJSON(item) : item,
        renew: renew,
        clear: clear
      };
      update.page = Annotate.page;
      if (deliver) {
        update.deliver = deliver;
      }
      if (storage) {
        update.storage = storage;
      } else if (update.item && update.item.annotationType === 'image') {
        delete update.item.src;
      }
      window.notifyUpdate(update);
    } catch (e) {
      console.log(e);
    }
  }

  static notifyPenEvent(item) {
    Annotate.notifyItemAction('pen', item, true, true);
  }

  static notifyAddItem(item) {
    Annotate.notifyItemAction('add', item, false, false, 'all', true);
  }

  static notifyLatestItem(item) {
    Annotate.notifyItemAction('update', item, true, false, 'ready');
  }

  static notifyRemoveItem(item) {
    Annotate.notifyItemAction('remove', item, false, true);
  }

  static notifyUpdateItem(item) {
    Annotate.notifyItemAction('update', item, true, false);
  }

  static updateActiveTool(pointerX, pointerY) {
    let canvas = Annotate.canvas;
    if (Annotate.activeTool === Annotate.ActiveTool.Text) {
      canvas.isDrawingMode = false;
      if (canvas.freeDrawingBrush.color === 'rgba(0, 0, 0, 0)') {
        Annotate.setColor('black');
      }

      let text = Annotate.createText({
        left: pointerX,
        top: pointerY,
        fill: Annotate.color,
        fontSize: Annotate.fontSize,
        selectable: true,
        annotationId: Annotate.uuid(),
        annotationType: 'text'
      });
      Annotate.notifyAddItem(text);

      Annotate.activeTool = Annotate.ActiveTool.Selector;
      Annotate.activeToolButton(Annotate.ActiveTool.Selector);
    } else if (Annotate.activeTool === Annotate.ActiveTool.Lead) {
      let lead = Annotate.createLead(pointerX, pointerY);
      Annotate.notifyAddItem(lead);
    }
  }

  static createPath(options) {
    let path = new fabric.Path(options.path, options);
    Annotate.canvas.add(path);
    return path;
  }

  static createText(options) {
    let text = new fabric.Textbox(options.text ? options.text : '添加文字', options);
    Annotate.canvas.add(text);
    let before = Annotate.toJSON(text);
    text.on('editing:exited', () => {
      Annotate.addUndo({
        id: text.annotationId,
        before: before,
        after: Annotate.toJSON(text)
      });
    });
    text.on('changed', () => {
      $('.controlPanel').remove();
      text.set('fill', Annotate.color);
      text.set('fontSize', Annotate.fontSize);
      Annotate.notifyUpdateItem({
        annotationId: text.annotationId,
        annotationType: text.annotationType,
        text: text.text
      });
      Annotate.notifyLatestItem(text);
    });
    text.on('mousedblclick', () => {
      before = Annotate.toJSON(text);
      text.enterEditing();
      text.selectAll();
    });
    text.on('mouseout', () => {});
    text.on('mouseon', () => {
      Annotate.mouseOutFlag = false;
    });
    return text;
  }

  static createLeadWithOptions(options) {
    let canvas = Annotate.canvas;
    let lead = new fabric.Path(
      'M 0 30 L 30 0 M 30 0 L 130 0 M 0 30 L 5 20  M 0 30 L 10 25 L 5 20',
      options
    );
    canvas.add(lead);
    return lead;
  }

  static createLead(left, top) {
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
    let leadBordersize = Annotate.borderSize >= 2 ? 2 : 1;
    let lead = Annotate.createLeadWithOptions({
      annotationId: Annotate.uuid(),
      annotationType: 'lead',
      left: left,
      top: top,
      fill: Annotate.borderColor,
      stroke: Annotate.borderColor,
      strokeWidth: leadBordersize
    });
    return lead;
  }

  static createCircleWithOptions(options) {
    let circle = new fabric.Circle(options);
    Annotate.canvas.add(circle);
    return circle;
  }

  static createCircle(mouseFrom, mouseTo) {
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
    let left = mouseFrom.x,
      top = mouseFrom.y;
    let radius =
      Math.sqrt((mouseTo.x - left) * (mouseTo.x - left) + (mouseTo.y - top) * (mouseTo.y - top)) /
      2;
    let circle = Annotate.createCircleWithOptions({
      left: left,
      top: top,
      fill: Annotate.fillColor,
      stroke: Annotate.borderColor,
      radius: radius,
      strokeWidth: Annotate.borderSize,
      annotationId: Annotate.uuid(),
      annotationType: 'circle'
    });
    return circle;
  }

  static createRectWithOptions(options) {
    let rect = new fabric.Rect(options);
    Annotate.canvas.add(rect);
    return rect;
  }

  static createRect(mouseFrom, mouseTo) {
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
    let left = mouseFrom.x,
      top = mouseFrom.y;
    let width = mouseTo.x - left,
      height = mouseTo.y - top;
    let rect = Annotate.createRectWithOptions({
      left: left,
      top: top,
      width: width,
      height: height,
      fill: Annotate.fillColor,
      stroke: Annotate.borderColor,
      strokeWidth: Annotate.borderSize,
      annotationId: Annotate.uuid(),
      annotationType: 'rect'
    });
    return rect;
  }

  static createImage(options) {
    return new Promise(resolve => {
      let image = new Image();
      if (options.annotationId && Annotate.storages[options.annotationId]) {
        image.src = Annotate.storages[options.annotationId].src;
        delete Annotate.storages[options.annotationId];
      } else {
        image.src = options.src;
      }

      image.onload = function () {
        let img = new fabric.Image(image, options);
        Annotate.canvas.add(img);
        resolve(img);
      };
    });
  }

  static enableSelector() {
    Annotate.activeTool = Annotate.ActiveTool.Selector;
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
  }

  static enableAddText() {
    Annotate.activeTool = Annotate.ActiveTool.Text;
    Annotate.activeToolButton(Annotate.ActiveTool.Text);
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
  }

  static enablePencil() {
    Annotate.activeTool = Annotate.ActiveTool.Pencil;
    Annotate.activeToolButton(Annotate.ActiveTool.Pencil);

    if (Annotate.canvas) {
      Annotate.canvas.freeDrawingBrush.width = Annotate.borderSize;
      Annotate.canvas.isDrawingMode = true;
      if (Annotate.canvas.freeDrawingBrush.color === 'rgba(0, 0, 0, 0)') {
        Annotate.setColor('black');
      }
    }
  }

  static enableAddMiddleText(left, top, text, canvas, object) {
    let exist = false;
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if ('comment' + object.annotationId === obj.annotationId) {
        let before = Annotate.toJSON(obj);
        obj.text = text;
        obj.fill = Annotate.color;
        obj.fontSize = Annotate.fontSize;
        Annotate.addUndo({
          id: obj.annotationId,
          before: before,
          after: Annotate.toJSON(obj)
        });
        Annotate.notifyUpdateItem({
          annotationId: obj.annotationId,
          annotationType: obj.annotationType,
          text: obj.text
        });
        Annotate.notifyLatestItem(obj);
        exist = true;
      }
    });
    if (!exist) {
      let textItem = Annotate.createText({
        left: left,
        top: top,
        text: text,
        fill: Annotate.color,
        fontSize: Annotate.fontSize,
        selectable: true,
        annotationType: 'comment',
        annotationId: 'comment' + object.annotationId
      });
      textItem.left = textItem.left - textItem.width / 2;
      textItem.top = textItem.top - textItem.height / 2;
      Annotate.notifyAddItem(textItem);
    }
    Annotate.renderAll();
    Annotate.activeTool = Annotate.ActiveTool.Selector;
    Annotate.activeToolButton(Annotate.ActiveTool.Selector);
  }

  static toObject(obj) {
    if (obj) {
      return obj.toObject(['annotationId', 'annotationType']);
    }
    return null;
  }

  static toJSON(obj) {
    if (obj) {
      return obj.toJSON(['annotationId', 'annotationType']);
    }
    return null;
  }

  static createControlPanel(canvas, object) {
    if (object.annotationType !== 'text' && object.annotationType !== 'comment') {
      $('.controlPanel').remove();
      $(
        '<div class="controlPanel" id="' +
          object.annotationId +
          '" style="display: block;">' +
          '<div class="controlPanel-delete">' +
          '<i class="fa fa-trash-o" aria-hidden="true"></i>' +
          '</div>' +
          '<div class="controlPanel-conment">' +
          '<i class="fa fa-comment-o" aria-hidden="true"></i>' +
          '</div></div>'
      ).appendTo($('.canvas-container'));
      let objWidth = object.width * object.scaleX;
      if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
        $('.controlPanel').css('left', object.left * Annotate.scale - 35);
      } else {
        $('.controlPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
      }
      $('.controlPanel').css('top', object.top * Annotate.scale - 5);
    } else if (object.annotationType === 'text') {
      $('.controlPanel').remove();
      $(
        '<div class="controlPanel" id="controlPanel' +
          object.annotationId +
          '" style="display: block;">' +
          '<div class="controlPanel-delete">' +
          '<i class="fa fa-trash-o" aria-hidden="true"></i>' +
          '</div></div>'
      ).appendTo($('.canvas-container'));
      let objWidth = object.width * object.scaleX;
      if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
        $('.controlPanel').css('left', object.left * Annotate.scale - 35);
      } else {
        $('.controlPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
      }
      $('.controlPanel').css('top', object.top * Annotate.scale - 5);
    }
  }

  static createCommentPanel(canvas, object) {
    $('.conmentPanel').remove();
    $(
      '<div class="conmentPanel" id=conmentPanel' +
        object.annotationId +
        ' style="display: block;">' +
        '<textarea></textarea>' +
        '<div class="conmentPanel-bottom">' +
        '<div id="conmentPanel-bottom-save">' +
        '<i class="fa fa-check fa-lg" aria-hidden="true"></i>' +
        '</div>' +
        '<div id="conmentPanel-bottom-cancel">' +
        '<i class="fa fa-times fa-lg" aria-hidden="true"></i>' +
        '</div>' +
        '</div></div>'
    ).appendTo($('.canvas-container'));
    let objWidth = object.width * object.scaleX;
    if ((object.left + object.width / 2) * Annotate.scale >= canvas.width / 2) {
      $('.conmentPanel').css('left', object.left * Annotate.scale - 205);
    } else {
      $('.conmentPanel').css('left', (object.left + objWidth) * Annotate.scale + 5);
    }
    $('.conmentPanel').css('top', object.top * Annotate.scale);
  }

  static deleteSelectedObject() {
    let canvas = Annotate.canvas;
    let activeObject = Annotate.canvas.getActiveObject();
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if ('comment' + activeObject.annotationId === obj.annotationId) {
        Annotate.canvas.remove(obj);
        Annotate.notifyRemoveItem(obj);
      }
    });

    if (activeObject['_objects'] !== undefined) {
      let etCount = activeObject['_objects'].length;
      for (let etindex = 0; etindex < etCount; etindex++) {
        canvas.remove(activeObject['_objects'][etindex]);
      }

      if (activeObject.annotationType === 'lead') {
        canvas.remove(activeObject);
      }
      canvas.discardActiveObject();
    } else {
      canvas.remove(activeObject);
    }
    canvas.requestRenderAll();
    Annotate.updateUndoRedoCss();
    Annotate.notifyRemoveItem(activeObject);
    return activeObject;
  }

  static setBrushSize(size) {
    if (Annotate.canvas) {
      Annotate.canvas.freeDrawingBrush.width = size;
    }
  }

  static setColor(color) {
    Annotate.color = color;
    if (Annotate.canvas) {
      Annotate.canvas.freeDrawingBrush.color = color;
    }
  }

  static setBorderColor(color) {
    Annotate.borderColor = color;
  }

  static setFontSize(size) {
    Annotate.fontSize = size;
    Annotate.canvas.discardActiveObject();
    Annotate.renderAll();
  }

  static setBorderSize(size) {
    Annotate.borderSize = size;
  }

  static loadFromJSON(jsonData) {
    if (Annotate.canvas) {
      Annotate.canvas.loadFromJSON(jsonData, () => {});
      Annotate.renderAll();
    }
  }

  static addImageToCanvas() {
    let canvas = Annotate.canvas;
    Annotate.activeTool = Annotate.ActiveTool.Image;
    if (canvas) {
      let inputElement = document.createElement('input');
      inputElement.type = 'file';
      inputElement.accept = 'image/*';
      inputElement.onchange = () => {
        let reader = new FileReader();
        reader.addEventListener(
          'load',
          () => {
            inputElement.remove();
            let id = Annotate.uuid();
            Annotate.notifyAddItem({
              annotationType: 'image',
              annotationId: id,
              src: reader.result
            });
            Annotate.createImage({
              annotationType: 'image',
              annotationId: id,
              src: reader.result
            });
          },
          false
        );
        reader.readAsDataURL(inputElement.files[0]);
      };
      inputElement.click();
    }
  }

  static renderAll() {
    Annotate.canvas.renderAll();
  }

  static uuid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
      let r = (Math.random() * 16) | 0,
        v = c === 'x' ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
  }

  static async recoveryDeleteItem(item) {
    let obj = null;
    Annotate.undo = false;
    if (item.annotationType === 'text') {
      obj = Annotate.createText(item);
    } else if (item.annotationType === 'path') {
      obj = Annotate.createPath(item);
    } else if (item.annotationType === 'rect') {
      obj = Annotate.createRectWithOptions(item);
    } else if (item.annotationType === 'circle') {
      obj = Annotate.createCircleWithOptions(item);
    } else if (item.annotationType === 'image') {
      obj = await Annotate.createImage(item);
    } else if (item.annotationType === 'lead') {
      obj = Annotate.createLeadWithOptions(item);
    } else if (item.annotationType === 'comment') {
      obj = Annotate.createText(item);
      obj.left = item.left - obj.width / 2;
      obj.top = item.top - obj.height / 2;
    } else {
      Annotate.undo = true;
      console.log('cannot recover item type:', item.annotationType);
    }
    if (obj) {
      Annotate.notifyAddItem(obj);
    }
  }

  static addUndo(step) {
    Annotate.undoOperation.push(step);
    Annotate.updateUndoRedoCss();
  }

  static addRedo(step) {
    Annotate.redoOperation.push(step);
    Annotate.updateUndoRedoCss();
  }

  static updateUndoRedoCss() {
    if (Annotate.undoOperation.length <= 0) {
      $('#undoButton i').css('color', 'white');
    } else {
      $('#undoButton i').css('color', 'black');
    }

    if (Annotate.redoOperation.length <= 0) {
      $('#redoButton i').css('color', 'white');
    } else {
      $('#redoButton i').css('color', 'black');
    }
  }

  static activeToolButton(buttonIndex) {
    $('.toolbar .tool .tool-button').attr('class', 'tool-button');
    $('.toolbar .tool .tool-button').eq(buttonIndex).attr('class', 'tool-button active');
  }

  static transformMouse(mouseX, mouseY) {
    return {
      x: mouseX / Annotate.scale,
      y: mouseY / Annotate.scale
    };
  }

  static setZoom(scale) {
    Annotate.scale = scale;
    if (Annotate.canvas) {
      Annotate.canvas.setZoom(scale);
    }
  }

  static setDimensions(dimensions) {
    if (Annotate.canvas) {
      Annotate.canvas.setDimensions(dimensions);
    }
  }
}

if (Annotate.hasBordersAndControls) {
  fabric.Object.prototype.set(Annotate.bordersAndControlsOptions);
} else {
  fabric.Object.prototype.set({
    hasControls: false,
    hasBorders: false
  });
}

function changeActiveTool(event) {
  let element = $(event.target).hasClass('tool-button')
    ? $(event.target)
    : $(event.target).parents('.tool-button').first();
  $('.tool-button.active').removeClass('active');
  $(element).addClass('active');
}

// eslint-disable-next-line no-unused-vars
function enablePencil(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.enablePencil();
}

// eslint-disable-next-line no-unused-vars
function enableAddText(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.enableAddText();
}

// eslint-disable-next-line no-unused-vars
function enableSelector(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.enableSelector();
}

// eslint-disable-next-line no-unused-vars
function enableRectangle(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.activeTool = Annotate.ActiveTool.Rectangle;
  if (Annotate.canvas) {
    Annotate.canvas.isDrawingMode = false;
  }
}

// eslint-disable-next-line no-unused-vars
function enableCircle(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.activeTool = Annotate.ActiveTool.Circle;
  if (Annotate.canvas) {
    Annotate.canvas.isDrawingMode = false;
  }
}

// eslint-disable-next-line no-unused-vars
function enableLead(event) {
  event.preventDefault();
  changeActiveTool(event);
  Annotate.activeTool = Annotate.ActiveTool.Lead;
  if (Annotate.canvas) {
    Annotate.canvas.isDrawingMode = false;
  }
}

// eslint-disable-next-line no-unused-vars
async function undo() {
  Annotate.enableSelector();
  if (Annotate.undoOperation.length === 0) {
    $('#undoButton i').css('color', 'white');
  } else {
    $('#undoButton').css('cursor', 'pointer');
    let currentOperation = Annotate.undoOperation.pop();
    Annotate.addRedo(currentOperation);
    Annotate.canvas.discardActiveObject();
    let exist = false;
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if (currentOperation.id === obj.annotationId) {
        exist = true;
        if (currentOperation.before === null) {
          Annotate.canvas.setActiveObject(obj);
          Annotate.undo = false;
          Annotate.deleteSelectedObject();
        } else {
          Annotate.undo = false;
          let updateData = {
            annotationId: obj.annotationId,
            annotationType: obj.annotationType
          };
          $.each(currentOperation.before, (key, val) => {
            if (obj[key] !== val) {
              obj.set(key, val);
              updateData[key] = val;
            }
          });
          Annotate.movingComment(obj);
          Annotate.notifyUpdateItem(updateData);
          Annotate.notifyLatestItem(obj);
          Annotate.canvas.setActiveObject(obj);
        }
        return false;
      }
    });
    if (!exist && currentOperation.after === null) {
      Annotate.recoveryDeleteItem(currentOperation.before);
    } else if (!exist) {
      console.log('unknow operation', currentOperation);
    }
  }
  Annotate.canvas.requestRenderAll();
}

// eslint-disable-next-line no-unused-vars
async function redo() {
  Annotate.enableSelector();
  if (Annotate.redoOperation.length === 0) {
    $('#redoButton i').css('color', 'white');
  } else {
    $('#undoButton').css('cursor', 'pointer');
    let currentOperation = Annotate.redoOperation.pop();
    Annotate.addUndo(currentOperation);
    Annotate.canvas.discardActiveObject();
    let exist = false;
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if (currentOperation.id === obj.annotationId) {
        exist = true;
        if (currentOperation.after === null) {
          Annotate.canvas.setActiveObject(obj);
          Annotate.deleteSelectedObject();
        } else {
          Annotate.undo = false;
          let updateData = {
            annotationId: obj.annotationId,
            annotationType: obj.annotationType
          };
          $.each(currentOperation.after, (key, val) => {
            if (obj[key] !== val) {
              obj.set(key, val);
              updateData[key] = val;
            }
          });
          Annotate.movingComment(obj);
          Annotate.notifyUpdateItem(updateData);
          Annotate.notifyLatestItem(obj);
          Annotate.canvas.setActiveObject(obj);
        }
        return false;
      }
    });
    if (!exist && currentOperation.before === null) {
      Annotate.recoveryDeleteItem(currentOperation.after);
    } else if (!exist) {
      console.log('unknow operation', currentOperation);
    }
    Annotate.canvas.requestRenderAll();
  }
}

// eslint-disable-next-line no-unused-vars
function addImage(event) {
  event.preventDefault();
  Annotate.addImageToCanvas();
}

$(() => {
  $('#brush-size').change(() => {
    let width = $('#brush-size').val();
    Annotate.setBrushSize(width);
    Annotate.setBorderSize(parseInt(width));
  });
  $('#brush-size').keyup(function () { 
    var value = $('#brush-size').val();
    value = value.replace(/[^0-9\.]/g,'');
    if (value < 1) {
        value = 1;
    }
    if (value > 50) {
        value = 50;
    }
    $('#brush-size').val(value)
  });

  $('#brush-size').on('focus', () => {
    if (Annotate.canvas) {
      Annotate.canvas.isDrawingMode = false;
    }
    Annotate.activeTool = Annotate.ActiveTool.Selector;
    Annotate.activeToolButton(Annotate.ActiveTool.Selector);
  });

  $('#font-size').change(() => {
    let fontSize = $('#font-size').val();
    Annotate.setFontSize(fontSize);
  });

  $('body').on('click', '.controlPanel-delete', event => {
    event.preventDefault();
    let obj = Annotate.deleteSelectedObject();
    Annotate.addUndo({
      id: obj.annotationId,
      before: obj,
      after: null
    });
  });

  $('body').on('click', '.controlPanel-conment', () => {
    let commentPanelId = $('.controlPanel').attr('id');
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if (commentPanelId === obj.annotationId) {
        Annotate.createCommentPanel(Annotate.canvas, obj);
      }
    });
  });

  $('body').on('click', '.controlPanel-conment', () => {
    let commentPanelId = $('.controlPanel').attr('id');
    $.each(Annotate.canvas.getObjects(), (index, obj) => {
      if (commentPanelId === obj.annotationId) {
        Annotate.createCommentPanel(Annotate.canvas, obj);
      }
    });
  });

  $('body').on('click', '#conmentPanel-bottom-cancel', () => {
    $('.conmentPanel').remove();
  });

  $('body').on('click', '#conmentPanel-bottom-save', () => {
    let text = $('.conmentPanel textarea').val();
    if (text !== '') {
      let commentPanelId = $('.controlPanel').attr('id');

      $.each(Annotate.canvas.getObjects(), (index, obj) => {
        if (commentPanelId === obj.annotationId) {
          let objLeft = obj.left + (obj.width * obj.scaleX) / 2;
          let objTop = obj.top + (obj.height * obj.scaleY) / 2;
          Annotate.enableAddMiddleText(objLeft, objTop, text, Annotate.canvas, obj);
        }
      });
      Annotate.canvas.discardActiveObject();
    }

    $('.conmentPanel').remove();
  });

  $('.color-plate').hide();
  $('.middle').hide();

  $('#undoButton').mouseover(() => {
    if (Annotate.undoOperation <= 0) {
      $('#undoButton').css('cursor', 'not-allowed');
    } else {
      $('#undoButton').css('cursor', 'pointer');
    }
  });
  $('#redoButton').mouseover(() => {
    if (Annotate.redoOperation <= 0) {
      $('#redoButton').css('cursor', 'not-allowed');
    } else {
      $('#redoButton').css('cursor', 'pointer');
    }
  });

  $('body').on('click', '.color-plate-item', e => {
    let backgroundColor = $(e.currentTarget).css('background-color');
    $('.toolbar-button-drop-button').css('background-color', backgroundColor);
    Annotate.setBorderColor(backgroundColor);
    Annotate.setColor(backgroundColor);
    $('.color-plate').toggle(200);
    let activeObject = Annotate.canvas.getActiveObject();
    if (activeObject) {
      let before = Annotate.toJSON(activeObject);
      if (activeObject.annotationType === 'image') {
        return;
      } else if (
        activeObject.annotationType === 'comment' ||
        activeObject.annotationType === 'text'
      ) {
        activeObject.set('fill', backgroundColor);
        let after = Annotate.toJSON(activeObject);
        if (JSON.stringify(before) !== JSON.stringify(after)) {
          Annotate.addUndo({
            id: after.annotationId,
            before: before,
            after: after
          });
          Annotate.notifyUpdateItem({
            annotationId: after.annotationId,
            annotationType: after.annotationType,
            fill: after.fill
          });
          Annotate.notifyLatestItem(activeObject);
        }
        Annotate.renderAll();
      } else {
        activeObject.set('stroke', backgroundColor);
        let after = Annotate.toJSON(activeObject);
        if (JSON.stringify(before) !== JSON.stringify(after)) {
          Annotate.addUndo({
            id: after.annotationId,
            before: before,
            after: after
          });
          Annotate.notifyUpdateItem({
            annotationId: after.annotationId,
            annotationType: after.annotationType,
            stroke: after.stroke
          });
          Annotate.notifyLatestItem(activeObject);
        }
        Annotate.renderAll();
      }
    }
  });

  $('.toolbar-button-drop-button').click(() => {
    $('.color-plate').toggle(200);
  });
});
